package com.csun.cmny.provider.device.watch;

import com.csun.cmny.provider.device.watch.entity.WatchConstant;
import com.csun.cmny.provider.device.watch.entity.WatchDataEntity;
import com.csun.cmny.provider.model.enums.device.DeviceStatusEnum;
import com.csun.cmny.util.PublicUtil;
import com.csun.cmny.util.RedisKeyUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @Auther: chisj chisj@foxmal.com
 * @Date: 2019-02-20 15:07
 * @Description: the watch netty tcp server handler
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class WatchNettyTcpServerHandler extends ChannelInboundHandlerAdapter {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private WatchMessageHandler watchMessageHandler;

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        log.info("# client registered...：   [{}] ...", ctx.channel());
    };

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            ByteBuf in = (ByteBuf) msg;
            String receiveMsg = in.toString(CharsetUtil.UTF_8);
            log.info("=====>    watch channel read msg: {}", receiveMsg);
            watchMessageHandler.handler(ctx, receiveMsg);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("=====>  watch channel read error: {}", e.getMessage());
        } finally {
            // 抛弃收到的数据
            ReferenceCountUtil.release(msg);
        }
    }

    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        log.info("watch netty tcp channel inactive... {}", ctx.toString());

        dealWatchOff(ctx);

        ctx.close();
    }

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        log.info("watch netty tcp exception caught... {}, cause = {}", ctx.toString(), cause.getMessage());

        dealWatchOff(ctx);

        ctx.close();
    }

    public void closeConnection(ChannelHandlerContext ctx) {

        log.info("watch netty tcp close... {}, ", ctx.toString());

        ctx.close();

    }

    /**
     * 这里是保持服务器与客户端长连接  进行心跳检测 避免连接断开
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent) {
            IdleStateEvent stateEvent = (IdleStateEvent) evt;
            PingWebSocketFrame ping = new PingWebSocketFrame();
            switch (stateEvent.state()){
                //读空闲（服务器端）
                case READER_IDLE:
                    log.info("===> watch netty tcp【"+ctx.channel().remoteAddress()+"】读空闲（服务器端）");
                    ctx.writeAndFlush(ping);
                    break;
                //写空闲（客户端）
                case WRITER_IDLE:
                    log.info("===> watch netty tcp【"+ctx.channel().remoteAddress()+"】写空闲（客户端）");
                    ctx.writeAndFlush(ping);
                    break;
                case ALL_IDLE:
                    log.info("===> watch netty tcp【"+ctx.channel().remoteAddress()+"】读写空闲");
                    break;
            }
        }
    }

    public void dealWatchOff(ChannelHandlerContext ctx) {

        try {
            WatchDataEntity watchDataEntity = ctx.channel().attr(WatchConstant.WATCH_CHANNEL_KEY).get();
            if (PublicUtil.isNotEmpty(watchDataEntity)) {
                String deviceNo = watchDataEntity.getDeviceNo();
                String redisKey = RedisKeyUtil.getXxWatchKey(deviceNo);
                watchDataEntity = (WatchDataEntity) redisTemplate.opsForValue().get(redisKey);
                if (PublicUtil.isNotEmpty(watchDataEntity)) {

                    watchDataEntity.setStatus(DeviceStatusEnum.OFFLINE.getKey());
                    watchDataEntity.setWearState(0);

                    redisTemplate.opsForValue().set(redisKey, watchDataEntity);

                    // 更新数据
                    watchMessageHandler.updateWatchData(watchDataEntity);

                    WatchConstant.WATCH_CHANNEL_MAP.remove(watchDataEntity.getDeviceNo());
                } else {
                    log.info("=====> watch {} is be break.", deviceNo);
                }
            } else {
                log.info("=====> watch channel not login.");
            }
        } catch (Exception e) {
            log.error("=====> deal watch off error: ", e.getMessage());
        }

    }
}
